/*
 * Copyright 1999-2017 Alibaba Group Holding Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package studio.raptor.sqlparser.dialect.mysql.parser;

import studio.raptor.sqlparser.ast.SQLDataTypeImpl;
import studio.raptor.sqlparser.ast.SQLExpr;
import studio.raptor.sqlparser.ast.SQLName;
import studio.raptor.sqlparser.ast.SQLOrderingSpecification;
import studio.raptor.sqlparser.ast.SQLPartition;
import studio.raptor.sqlparser.ast.SQLPartitionBy;
import studio.raptor.sqlparser.ast.SQLPartitionByHash;
import studio.raptor.sqlparser.ast.SQLPartitionByList;
import studio.raptor.sqlparser.ast.SQLPartitionByRange;
import studio.raptor.sqlparser.ast.SQLSubPartitionBy;
import studio.raptor.sqlparser.ast.SQLSubPartitionByHash;
import studio.raptor.sqlparser.ast.expr.SQLIdentifierExpr;
import studio.raptor.sqlparser.ast.expr.SQLIntegerExpr;
import studio.raptor.sqlparser.ast.expr.SQLNumberExpr;
import studio.raptor.sqlparser.ast.statement.SQLAssignItem;
import studio.raptor.sqlparser.ast.statement.SQLCheck;
import studio.raptor.sqlparser.ast.statement.SQLColumnDefinition;
import studio.raptor.sqlparser.ast.statement.SQLCreateTableStatement;
import studio.raptor.sqlparser.ast.statement.SQLForeignKeyConstraint;
import studio.raptor.sqlparser.ast.statement.SQLSelect;
import studio.raptor.sqlparser.ast.statement.SQLTableConstraint;
import studio.raptor.sqlparser.ast.statement.SQLTableSource;
import studio.raptor.sqlparser.dialect.mysql.ast.MySqlKey;
import studio.raptor.sqlparser.dialect.mysql.ast.MySqlPrimaryKey;
import studio.raptor.sqlparser.dialect.mysql.ast.MySqlUnique;
import studio.raptor.sqlparser.dialect.mysql.ast.MysqlForeignKey;
import studio.raptor.sqlparser.dialect.mysql.ast.expr.MySqlOrderingExpr;
import studio.raptor.sqlparser.dialect.mysql.ast.statement.MySqlCreateTableStatement;
import studio.raptor.sqlparser.dialect.mysql.ast.statement.MySqlCreateTableStatement.TableSpaceOption;
import studio.raptor.sqlparser.dialect.mysql.ast.statement.MySqlPartitionByKey;
import studio.raptor.sqlparser.dialect.mysql.ast.statement.MySqlSubPartitionByKey;
import studio.raptor.sqlparser.dialect.mysql.ast.statement.MySqlSubPartitionByList;
import studio.raptor.sqlparser.dialect.mysql.ast.statement.MySqlTableIndex;
import studio.raptor.sqlparser.parser.ParserException;
import studio.raptor.sqlparser.parser.SQLCreateTableParser;
import studio.raptor.sqlparser.parser.SQLExprParser;
import studio.raptor.sqlparser.parser.Token;

public class MySqlCreateTableParser extends SQLCreateTableParser {

  public MySqlCreateTableParser(String sql) {
    super(new MySqlExprParser(sql));
  }

  public MySqlCreateTableParser(SQLExprParser exprParser) {
    super(exprParser);
  }

  public SQLCreateTableStatement parseCreateTable() {
    return parseCreateTable(true);
  }

  public MySqlExprParser getExprParser() {
    return (MySqlExprParser) exprParser;
  }

  public MySqlCreateTableStatement parseCreateTable(boolean acceptCreate) {
    if (acceptCreate) {
      accept(Token.CREATE);
    }
    MySqlCreateTableStatement stmt = new MySqlCreateTableStatement();

    if (identifierEquals("TEMPORARY")) {
      lexer.nextToken();
      stmt.setType(SQLCreateTableStatement.Type.GLOBAL_TEMPORARY);
    }

    accept(Token.TABLE);

    if (lexer.token() == Token.IF || identifierEquals("IF")) {
      lexer.nextToken();
      accept(Token.NOT);
      accept(Token.EXISTS);

      stmt.setIfNotExiists(true);
    }

    stmt.setName(this.exprParser.name());

    if (lexer.token() == Token.LIKE) {
      lexer.nextToken();
      SQLName name = this.exprParser.name();
      stmt.setLike(name);
    }

    if (lexer.token() == (Token.LPAREN)) {
      lexer.nextToken();

      if (lexer.token() == Token.LIKE) {
        lexer.nextToken();
        SQLName name = this.exprParser.name();
        stmt.setLike(name);
      } else {
        for (; ; ) {
          if (lexer.token() == Token.IDENTIFIER //
              || lexer.token() == Token.LITERAL_CHARS) {
            SQLColumnDefinition column = this.exprParser.parseColumn();
            stmt.getTableElementList().add(column);
          } else if (lexer.token() == Token.CONSTRAINT //
              || lexer.token() == Token.PRIMARY //
              || lexer.token() == Token.UNIQUE) {
            SQLTableConstraint constraint = this.parseConstraint();
            stmt.getTableElementList().add(constraint);
          } else if (lexer.token() == (Token.INDEX)) {
            lexer.nextToken();

            MySqlTableIndex idx = new MySqlTableIndex();

            if (lexer.token() == Token.IDENTIFIER) {
              if (!"USING".equalsIgnoreCase(lexer.stringVal())) {
                idx.setName(this.exprParser.name());
              }
            }

            if (identifierEquals("USING")) {
              lexer.nextToken();
              idx.setIndexType(lexer.stringVal());
              lexer.nextToken();
            }

            accept(Token.LPAREN);
            for (; ; ) {
              idx.addColumn(this.exprParser.expr());
              if (!(lexer.token() == (Token.COMMA))) {
                break;
              } else {
                lexer.nextToken();
              }
            }
            accept(Token.RPAREN);

            if (identifierEquals("USING")) {
              lexer.nextToken();
              idx.setIndexType(lexer.stringVal());
              lexer.nextToken();
            }

            stmt.getTableElementList().add(idx);
          } else if (lexer.token() == (Token.KEY)) {
            stmt.getTableElementList().add(parseConstraint());
          } else if (lexer.token() == (Token.PRIMARY)) {
            SQLTableConstraint pk = parseConstraint();
            pk.setParent(stmt);
            stmt.getTableElementList().add(pk);
          } else if (lexer.token() == (Token.FOREIGN)) {
            SQLForeignKeyConstraint fk = this.getExprParser().parseForeignKey();
            fk.setParent(stmt);
            stmt.getTableElementList().add(fk);
          } else if (lexer.token() == Token.CHECK) {
            SQLCheck check = this.exprParser.parseCheck();
            stmt.getTableElementList().add(check);
          } else {
            SQLColumnDefinition column = this.exprParser.parseColumn();
            stmt.getTableElementList().add(column);
          }

          if (!(lexer.token() == (Token.COMMA))) {
            break;
          } else {
            lexer.nextToken();
          }
        }
      }

      accept(Token.RPAREN);
    }

    for (; ; ) {
      if (identifierEquals("ENGINE")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.getTableOptions().put("ENGINE", this.exprParser.expr());
        continue;
      }

      if (identifierEquals("AUTO_INCREMENT")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.getTableOptions().put("AUTO_INCREMENT", this.exprParser.expr());
        continue;
      }

      if (identifierEquals("AVG_ROW_LENGTH")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.getTableOptions().put("AVG_ROW_LENGTH", this.exprParser.expr());
        continue;
      }

      if (lexer.token() == Token.DEFAULT) {
        lexer.nextToken();
        parseTableOptionCharsetOrCollate(stmt);
        continue;
      }

      if (parseTableOptionCharsetOrCollate(stmt)) {
        continue;
      }

      if (identifierEquals("CHECKSUM")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.getTableOptions().put("CHECKSUM", this.exprParser.expr());
        continue;
      }

      if (lexer.token() == Token.COMMENT) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.getTableOptions().put("COMMENT", this.exprParser.expr());
        continue;
      }

      if (identifierEquals("CONNECTION")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.getTableOptions().put("CONNECTION", this.exprParser.expr());
        continue;
      }

      if (identifierEquals("DATA")) {
        lexer.nextToken();
        acceptIdentifier("DIRECTORY");
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.getTableOptions().put("DATA DIRECTORY", this.exprParser.expr());
        continue;
      }

      if (identifierEquals("DELAY_KEY_WRITE")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.getTableOptions().put("DELAY_KEY_WRITE", this.exprParser.expr());
        continue;
      }

      if (identifierEquals("INDEX")) {
        lexer.nextToken();
        acceptIdentifier("DIRECTORY");
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.getTableOptions().put("INDEX DIRECTORY", this.exprParser.expr());
        continue;
      }

      if (identifierEquals("INSERT_METHOD")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.getTableOptions().put("INSERT_METHOD", this.exprParser.expr());
        continue;
      }

      if (identifierEquals("KEY_BLOCK_SIZE")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.getTableOptions().put("KEY_BLOCK_SIZE", this.exprParser.expr());
        continue;
      }

      if (identifierEquals("MAX_ROWS")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.getTableOptions().put("MAX_ROWS", this.exprParser.expr());
        continue;
      }

      if (identifierEquals("MIN_ROWS")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.getTableOptions().put("MIN_ROWS", this.exprParser.expr());
        continue;
      }

      if (identifierEquals("PACK_KEYS")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.getTableOptions().put("PACK_KEYS", this.exprParser.expr());
        continue;
      }

      if (identifierEquals("PASSWORD")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.getTableOptions().put("PASSWORD", this.exprParser.expr());
        continue;
      }

      if (identifierEquals("ROW_FORMAT")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.getTableOptions().put("ROW_FORMAT", this.exprParser.expr());
        continue;
      }

      if (identifierEquals("STATS_AUTO_RECALC")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }

        stmt.getTableOptions().put("STATS_AUTO_RECALC", this.exprParser.expr());
        continue;
      }

      if (identifierEquals("STATS_PERSISTENT")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }

        stmt.getTableOptions().put("STATS_PERSISTENT", this.exprParser.expr());
        continue;
      }

      if (identifierEquals("STATS_SAMPLE_PAGES")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }

        stmt.getTableOptions().put("STATS_SAMPLE_PAGES", this.exprParser.expr());
        continue;
      }

      if (lexer.token() == Token.UNION) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }

        accept(Token.LPAREN);
        SQLTableSource tableSrc = this.createSQLSelectParser().parseTableSource();
        stmt.getTableOptions().put("UNION", tableSrc);
        accept(Token.RPAREN);
        continue;
      }

      if (lexer.token() == Token.TABLESPACE) {
        lexer.nextToken();

        TableSpaceOption option = new TableSpaceOption();
        option.setName(this.exprParser.name());

        if (identifierEquals("STORAGE")) {
          lexer.nextToken();
          option.setStorage(this.exprParser.name());
        }

        stmt.getTableOptions().put("TABLESPACE", option);
        continue;
      }

      if (identifierEquals("TABLEGROUP")) {
        lexer.nextToken();

        SQLName tableGroup = this.exprParser.name();
        stmt.setTableGroup(tableGroup);
        continue;
      }

      if (identifierEquals("TYPE")) {
        lexer.nextToken();
        accept(Token.EQ);
        stmt.getTableOptions().put("TYPE", this.exprParser.expr());
        lexer.nextToken();
        continue;
      }

      if (lexer.token() == Token.PARTITION) {
        lexer.nextToken();
        accept(Token.BY);

        SQLPartitionBy partitionClause;

        boolean linera = false;
        if (identifierEquals("LINEAR")) {
          lexer.nextToken();
          linera = true;
        }

        if (lexer.token() == Token.KEY) {
          MySqlPartitionByKey clause = new MySqlPartitionByKey();
          lexer.nextToken();

          if (linera) {
            clause.setLinear(true);
          }

          accept(Token.LPAREN);
          if (lexer.token() != Token.RPAREN) {
            for (; ; ) {
              clause.addColumn(this.exprParser.name());
              if (lexer.token() == Token.COMMA) {
                lexer.nextToken();
                continue;
              }
              break;
            }
          }
          accept(Token.RPAREN);

          partitionClause = clause;

          partitionClauseRest(clause);
        } else if (identifierEquals("HASH")) {
          lexer.nextToken();
          SQLPartitionByHash clause = new SQLPartitionByHash();

          if (linera) {
            clause.setLinear(true);
          }

          if (lexer.token() == Token.KEY) {
            lexer.nextToken();
            clause.setKey(true);
          }

          accept(Token.LPAREN);
          clause.setExpr(this.exprParser.expr());
          accept(Token.RPAREN);
          partitionClause = clause;

          partitionClauseRest(clause);

        } else if (identifierEquals("RANGE")) {
          SQLPartitionByRange clause = partitionByRange();
          partitionClause = clause;

          partitionClauseRest(clause);

        } else if (identifierEquals("LIST")) {
          lexer.nextToken();
          SQLPartitionByList clause = new SQLPartitionByList();

          if (lexer.token() == Token.LPAREN) {
            lexer.nextToken();
            clause.setExpr(this.exprParser.expr());
            accept(Token.RPAREN);
          } else {
            acceptIdentifier("COLUMNS");
            accept(Token.LPAREN);
            for (; ; ) {
              clause.addColumn(this.exprParser.name());
              if (lexer.token() == Token.COMMA) {
                lexer.nextToken();
                continue;
              }
              break;
            }
            accept(Token.RPAREN);
          }
          partitionClause = clause;

          partitionClauseRest(clause);
        } else {
          throw new ParserException("TODO " + lexer.token() + " " + lexer.stringVal());
        }

        if (lexer.token() == Token.LPAREN) {
          lexer.nextToken();
          for (; ; ) {
            SQLPartition partitionDef = this.getExprParser().parsePartition();

            partitionClause.addPartition(partitionDef);

            if (lexer.token() == Token.COMMA) {
              lexer.nextToken();
              continue;
            } else {
              break;
            }
          }
          accept(Token.RPAREN);
        }

        stmt.setPartitioning(partitionClause);

        continue;
      }

      break;
    }

    if (lexer.token() == (Token.ON)) {
      throw new ParserException("TODO");
    }

    if (lexer.token() == (Token.AS)) {
      lexer.nextToken();
    }

    if (lexer.token() == (Token.SELECT)) {
      SQLSelect query = new MySqlSelectParser(this.exprParser).select();
      stmt.setSelect(query);
    }

    while (lexer.token() == (Token.HINT)) {
      this.exprParser.parseHints(stmt.getOptionHints());
    }
    return stmt;
  }


  protected SQLPartitionByRange partitionByRange() {
    acceptIdentifier("RANGE");

    SQLPartitionByRange clause = new SQLPartitionByRange();

    if (lexer.token() == Token.LPAREN) {
      lexer.nextToken();
      clause.setExpr(this.exprParser.expr());
      accept(Token.RPAREN);
    } else {
      acceptIdentifier("COLUMNS");
      accept(Token.LPAREN);
      for (; ; ) {
        clause.addColumn(this.exprParser.name());
        if (lexer.token() == Token.COMMA) {
          lexer.nextToken();
          continue;
        }
        break;
      }
      accept(Token.RPAREN);
    }
    return clause;
  }

  protected void partitionClauseRest(SQLPartitionBy clause) {
    if (identifierEquals("PARTITIONS")) {
      lexer.nextToken();

      SQLIntegerExpr countExpr = this.exprParser.integerExpr();
      clause.setPartitionsCount(countExpr);
    }

    if (lexer.token() == Token.PARTITION) {
      lexer.nextToken();

      if (identifierEquals("NUM")) {
        lexer.nextToken();
      }

      clause.setPartitionsCount(this.exprParser.expr());

      clause.putAttribute("ads.partition", Boolean.TRUE);
    }

    if (identifierEquals("SUBPARTITION")) {
      lexer.nextToken();
      accept(Token.BY);

      SQLSubPartitionBy subPartitionByClause = null;

      boolean linear = false;
      if (identifierEquals("LINEAR")) {
        lexer.nextToken();
        linear = true;
      }

      if (lexer.token() == Token.KEY) {
        MySqlSubPartitionByKey subPartitionKey = new MySqlSubPartitionByKey();
        lexer.nextToken();

        if (linear) {
          clause.setLinear(true);
        }

        accept(Token.LPAREN);
        for (; ; ) {
          subPartitionKey.addColumn(this.exprParser.name());
          if (lexer.token() == Token.COMMA) {
            lexer.nextToken();
            continue;
          }
          break;
        }
        accept(Token.RPAREN);

        subPartitionByClause = subPartitionKey;

      } else if (identifierEquals("HASH")) {
        lexer.nextToken();
        SQLSubPartitionByHash subPartitionHash = new SQLSubPartitionByHash();

        if (linear) {
          clause.setLinear(true);
        }

        if (lexer.token() == Token.KEY) {
          lexer.nextToken();
          subPartitionHash.setKey(true);
        }

        accept(Token.LPAREN);
        subPartitionHash.setExpr(this.exprParser.expr());
        accept(Token.RPAREN);
        subPartitionByClause = subPartitionHash;

      } else if (identifierEquals("LIST")) {
        lexer.nextToken();
        MySqlSubPartitionByList subPartitionList = new MySqlSubPartitionByList();

        if (lexer.token() == Token.LPAREN) {
          lexer.nextToken();
          SQLExpr expr = this.exprParser.expr();

          if (expr instanceof SQLIdentifierExpr && (identifierEquals("bigint") || identifierEquals(
              "long"))) {
            String dataType = lexer.stringVal();
            lexer.nextToken();

            SQLColumnDefinition column = this.exprParser.createColumnDefinition();
            column.setName((SQLIdentifierExpr) expr);
            column.setDataType(new SQLDataTypeImpl(dataType));
            subPartitionList.addColumn(column);

            subPartitionList.putAttribute("ads.subPartitionList", Boolean.TRUE);
          } else {
            subPartitionList.setExpr(expr);
          }
          accept(Token.RPAREN);
        } else {
          acceptIdentifier("COLUMNS");
          accept(Token.LPAREN);
          for (; ; ) {
            subPartitionList.addColumn(this.exprParser.parseColumn());
            if (lexer.token() == Token.COMMA) {
              lexer.nextToken();
              continue;
            }
            break;
          }
          accept(Token.RPAREN);
        }
        subPartitionByClause = subPartitionList;
      }

      if (identifierEquals("SUBPARTITION") && null != subPartitionByClause) {
        lexer.nextToken();
        acceptIdentifier("OPTIONS");
        accept(Token.LPAREN);

        SQLAssignItem option = this.exprParser.parseAssignItem();
        accept(Token.RPAREN);

        option.setParent(subPartitionByClause);

        subPartitionByClause.getOptions().add(option);
      }

      if (identifierEquals("SUBPARTITIONS") && null != subPartitionByClause) {
        lexer.nextToken();
        Number intValue = lexer.integerValue();
        SQLNumberExpr numExpr = new SQLNumberExpr(intValue);
        subPartitionByClause.setSubPartitionsCount(numExpr);
        lexer.nextToken();
      }

      if (subPartitionByClause != null) {
        subPartitionByClause.setLinear(linear);

        clause.setSubPartitionBy(subPartitionByClause);
      }
    }
  }

  private boolean parseTableOptionCharsetOrCollate(MySqlCreateTableStatement stmt) {
    if (identifierEquals("CHARACTER")) {
      lexer.nextToken();
      accept(Token.SET);
      if (lexer.token() == Token.EQ) {
        lexer.nextToken();
      }
      stmt.getTableOptions().put("CHARACTER SET", this.exprParser.expr());
      return true;
    }

    if (identifierEquals("CHARSET")) {
      lexer.nextToken();
      if (lexer.token() == Token.EQ) {
        lexer.nextToken();
      }
      stmt.getTableOptions().put("CHARSET", this.exprParser.expr());
      return true;
    }

    if (identifierEquals("COLLATE")) {
      lexer.nextToken();
      if (lexer.token() == Token.EQ) {
        lexer.nextToken();
      }
      stmt.getTableOptions().put("COLLATE", this.exprParser.expr());
      return true;
    }

    return false;
  }

  protected SQLTableConstraint parseConstraint() {
    SQLName name = null;
    boolean hasConstaint = false;
    if (lexer.token() == (Token.CONSTRAINT)) {
      hasConstaint = true;
      lexer.nextToken();
    }

    if (lexer.token() == Token.IDENTIFIER) {
      name = this.exprParser.name();
    }

    if (lexer.token() == (Token.KEY)) {
      lexer.nextToken();

      MySqlKey key = new MySqlKey();
      key.setHasConstaint(hasConstaint);

      // if (identifierEquals("USING")) {
      // lexer.nextToken();
      // key.setIndexType(lexer.stringVal());
      // lexer.nextToken();
      // }

      if (lexer.token() == Token.IDENTIFIER || lexer.token() == Token.LITERAL_ALIAS) {
        SQLName indexName = this.exprParser.name();
        if (indexName != null) {
          key.setIndexName(indexName);
        }
      }

      // 5.5语法 USING BTREE 放在index 名字后
      if (identifierEquals("USING")) {
        lexer.nextToken();
        key.setIndexType(lexer.stringVal());
        lexer.nextToken();
      }

      accept(Token.LPAREN);
      for (; ; ) {
        SQLExpr expr = this.exprParser.expr();
        if (lexer.token() == Token.ASC) {
          lexer.nextToken();
          expr = new MySqlOrderingExpr(expr, SQLOrderingSpecification.ASC);
        } else if (lexer.token() == Token.DESC) {
          lexer.nextToken();
          expr = new MySqlOrderingExpr(expr, SQLOrderingSpecification.DESC);
        }

        key.addColumn(expr);
        if (!(lexer.token() == (Token.COMMA))) {
          break;
        } else {
          lexer.nextToken();
        }
      }
      accept(Token.RPAREN);

      if (name != null) {
        key.setName(name);
      }

      if (identifierEquals("USING")) {
        lexer.nextToken();
        key.setIndexType(lexer.stringVal());
        lexer.nextToken();
      }

      return key;
    }

    if (lexer.token() == Token.PRIMARY) {
      MySqlPrimaryKey pk = this.getExprParser().parsePrimaryKey();
      pk.setName(name);
      pk.setHasConstaint(hasConstaint);
      return (SQLTableConstraint) pk;
    }

    if (lexer.token() == Token.UNIQUE) {
      MySqlUnique uk = this.getExprParser().parseUnique();
      uk.setName(name);
      uk.setHasConstaint(hasConstaint);
      return (SQLTableConstraint) uk;
    }

    if (lexer.token() == Token.FOREIGN) {
      MysqlForeignKey fk = this.getExprParser().parseForeignKey();
      fk.setName(name);
      fk.setHasConstraint(hasConstaint);
      return (SQLTableConstraint) fk;
    }

    throw new ParserException("TODO :" + lexer.token());
  }
}
